﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenTK;

using OpenTK.Graphics.OpenGL;
using Xeno.Module;
using Xeno.Maths;
using Xeno.Physics;
using OpenTK;
namespace Xeno.Core
{
    public class XenoNode
    {
        public static Dictionary<string, XenoNode> NodeMap = new Dictionary<string, XenoNode>();
        public Vector3 AngularDrag { get; set; }
        public Vector3 InertialDrag { get; set; }
     
    
        public object UserData { get; set; }
        public object UserData1 { get; set; }
        public object[] UserGrid { get; set; }
        public bool Active { get; set; }
        public XenoGraph GraphOwner { get; set; }
        public string Name { get; set; }
        public Vector3 LocalPosition { get; set; }
        public Vector3 LocalScale { get; set; }
        public bool HideInEditor { get; set; }
        public Vector3 WorldPosition
        {
            get
            {
                Vector3 pos = Vector3.Zero;
                if (Parent != null)
                {
                    pos = Parent.WorldPosition;
                }
                return pos + LocalPosition;
            }
        }
        public Matrix4 LocalRotation { get; set; }

        public Vector3 Direction
        {
            get
            {
                return Vector3.TransformVector(new Vector3(0, 0, 1), WorldMatrix);
            }
            set
            {
                value.X *= 10;
                value.Y *= 10;
                value.Z *= 10;

                LocalRotation = Matrix4.LookAt(Vector3.Zero, Direction, new Vector3(0,1,0));
            }
        }
        public Matrix4 WorldRotationMatrix
        {
            get
            {
                Matrix4 pm = Matrix4.Identity;

                if (Parent != null)
                {
                    pm = Parent.WorldMatrix;
                }

                if (Camera != null)
                {
                    if (Camera.InvertWorld)
                    {


                        Matrix4 tmp = (LocalRotation);
                        tmp.Invert();
                        return pm * tmp;

                    }
                }

                return (LocalRotation) * pm;
            }
        }
        public Matrix4 WorldMatrix
        {
            get
            {
                Matrix4 pm = Matrix4.Identity;

                if (Parent != null)
                {
                    pm = Parent.WorldMatrix;
                }

                if (Camera != null)
                {
                    if (Camera.InvertWorld)
                    {

                   
                        Matrix4 tmp = (Matrix4.Scale(LocalScale)*LocalRotation * Matrix4.CreateTranslation(LocalPosition));
                        tmp.Invert();
                        return pm*tmp;

                    }
                }

                return  (Matrix4.Scale(LocalScale)*LocalRotation*Matrix4.CreateTranslation(LocalPosition))*pm;


            }
        }

        public List<XenoNode> Nodes { get; set; }
        public XenoNode Parent { get; set; }
        public List<XenoModule> Modules { get; set; }
        public Vector3[] BoundingBox
        {

            get
            {
                bool first = true;
                float sx, sy, sz, bx, by, bz;
                bx = by = bz = sx = sy = sz = 0;
                sx = sy = sz = 999999;
                bx = by = bz = -999999;
               
                List<XenoNode> meshes = this.AllWithType(typeof(XenoMesh),null);
                foreach (var m in meshes)
                {
                    Vector3[] bb = m.Mesh.BoundingBox;
                  
                    if (bb[0].X < sx) sx = bb[0].X;
                    if (bb[0].Y < sy) sy = bb[0].Y;
                    if (bb[0].Z < sz) sz = bb[0].Z;
                    if (bb[0].X > bx) bx = bb[0].X;
                    if (bb[0].Y > by) by = bb[0].Y;
                    if (bb[0].Z > bz) bz = bb[0].Z;
                    if (bb[1].X > bx) bx = bb[1].X;
                    if (bb[1].Y > by) by = bb[1].Y;
                    if (bb[1].Z > bz) bz = bb[1].Z;
                    if (bb[1].X < sx) sx = bb[1].X;
                    if (bb[1].Y < sy) sy = bb[1].Y;
                    if (bb[1].Z < sz) bz = bb[1].Z;

                }
                Vector3[] ret = new Vector3[2];
          
                ret[0] = new Vector3(sx, sy, sz);
                ret[1] = new Vector3(bx, by, bz);
                return ret;
            }
        }
        public XenoModule AddModule
        {
            set
            {
                
                if (value is XenoMesh) Mesh = value as XenoMesh;
                if (value is XenoMod_Camera) Camera = value as XenoMod_Camera;
                if (value is XenoMeshRenderer) MeshRenderer = value as XenoMeshRenderer;
                if (value is XenoMod_Light) Light = value as XenoMod_Light;
                if (value is XenoPhysicsManager) PhysicsManager = value as XenoPhysicsManager;
                if (value is XenoCollider) Collider = value as XenoCollider;
                if (value is XuiWidget) Widget = value as XuiWidget;
                value.Attached = this;


                
                Modules.Add(value);

            }
        }


        public XenoMod_Camera Camera { get; private set; }
        public XenoMesh Mesh { get; private set; }
        public XenoMeshRenderer MeshRenderer { get; private set; }
        public XenoMod_Light Light { get; private set; }
        public XenoPhysicsManager PhysicsManager { get; private set; }
        public XenoCollider Collider { get; private set; }
        public XuiWidget Widget { get; private set; }
        public void Rotate(float pitch, float yaw, float roll)
        {
            Matrix4 tmp = Matrix4.RotateY(XenoMath.Deg2Rad(yaw));
            Matrix4 tmp2 = Matrix4.RotateX(XenoMath.Deg2Rad(pitch));
            LocalRotation = tmp2 * tmp * Matrix4.RotateZ(XenoMath.Deg2Rad(roll));

        }
        public void LookAt(Vector3 p)
        {
            LocalRotation = Matrix4.Invert(Matrix4.LookAt(Vector3.Zero, p - LocalPosition, Vector3.UnitY));
        }
        public List<XenoNode> AllWithType(Type type,List<XenoNode> addTo)
        {
            if (addTo == null)
            {
                addTo = new List<XenoNode>();
            }
            if (!Active)
            {
                return addTo;
            }
            foreach (XenoModule mod in Modules)
            {
                if (mod.GetType() == type)
                {
                    addTo.Add(this);
                    break;
                }
            }
            foreach (var node in Nodes)
            {
                node.AllWithType(type, addTo);
            }
            return addTo;
        }

        public XenoModule Add(XenoModule module)
        {
            AddModule = module;
            return module;
        }

        public XenoNode(string name)
        {
            Active = false;
            ResetNode();
            Name = name;
            NodeMap.Add(name, this);
            Active = true;
        }
        public XenoNode(string name,XenoNode parent)
        {
            Active = false;
            ResetNode();
            Name = name;
            parent.Nodes.Add(this);
            Parent = parent;
           
            GraphOwner = parent.GraphOwner;
    
            Active = true;
        }

        public XenoNode ResetNode()
        {
         
            LocalPosition = Vector3.Zero;
            LocalRotation = Matrix4.Identity;
            LocalScale = new Vector3(1, 1, 1);
            Nodes = new List<XenoNode>();
            Modules = new List<XenoModule>();
            Parent = null;
            HideInEditor = false;
            return this;
        }

        public void PerformUpdate()
        {
            if (!Active) return;
            foreach (var mod in Modules)
            {
                mod.PerformUpdate();
            }
            foreach (var node in Nodes)
            {
                node.PerformUpdate();
            }
        }

        public void PerformRender()
        {
            if (!Active) return;
            GL.PushAttrib(AttribMask.AllAttribBits);
            foreach (var mod in Modules)
            {
                mod.PerformRender();
            }
            foreach (var node in Nodes)
            {
                node.PerformRender();
            }
            GL.PopAttrib();
        }

    }
}
